home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 22 code / PCI Driver Sample / NCR_DriverProject / Src / NCRStartScript.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-04-05  |  14.6 KB  |  426 lines  |  [TEXT/MPCC]

  1. /*                                    NCRStartScript.c                                */
  2. /*
  3.  * NCRStartScript.c
  4.  * Copyright © 1994 Apple Computer Inc. All rights reserved.
  5.  */
  6. /*    .___________________________________________________________________________________.
  7.       | These low-level routines convert an I/O request into a form that can be handled    |
  8.       | by the NCR hardware. While this is primarily specific to the NCR chip, you should    |
  9.       | understand how the setup functions call PrepareMemoryForIO. This is a limited        |
  10.       | sample that does not handle scatter-gather I/O requests -- these will be needed    |
  11.       | in a production driver. However, they require a somewhat more complex NCR script;    |
  12.       | or, at the very least, more thought on my part.                                    |
  13.     .___________________________________________________________________________________.
  14. */
  15. #include "NCRDriverPrivate.h"
  16. #include <stddef.h>
  17.  
  18. OSErr                        NCRSetupDoSCSIScript(
  19.         PerRequestDataPtr        perRequestDataPtr
  20.     );
  21. ByteCount                    SetupSCSICommand(
  22.         PerRequestDataPtr        perRequestDataPtr
  23.     );
  24. ByteCount                    SCSIGetCommandLength(
  25.         const unsigned char        *cmdBlock
  26.     );
  27. OSErr                        NCRSetupTestISR(
  28.         PerRequestDataPtr        perRequestDataPtr
  29.     );
  30. OSErr                        NCRSetupTestMemory(
  31.         PerRequestDataPtr        perRequestDataPtr
  32.     );
  33.  
  34. /*
  35.  * All functions here use a standard variable set to access information.
  36.  * I've "globalized" this to minimize clutter.
  37.  */
  38. #define REQUEST            (*perRequestDataPtr)
  39. #define PB                (*((IOParam *) REQUEST.pb))
  40. #define SCSI            (*((NCRSCSIParamPtr) PB.ioMisc))
  41. #define SCRIPT            (REQUEST.scriptData)
  42. /*
  43.  * AddressOf is a physical address
  44.  */
  45. #define AddressOf(what)    (((UInt32) REQUEST.scriptDataPtr) + offsetof(ScriptData, what))
  46. /*
  47.  * SetTable (must be a macro) stores an address and byte count into a table.
  48.  */
  49. #define SetTable(whichTable, physAddress, dataCount) do {            \
  50.         SCRIPT.whichTable.address = EndianSwap32Bit((UInt32) physAddress);    \
  51.         SCRIPT.whichTable.byteCount = EndianSwap32Bit(dataCount);            \
  52.     } while (0)
  53.  
  54. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  55.  * NCRStartScript starts a Script operation. The script completes through a Secondary
  56.  * Interrupt Handler.
  57.  */
  58. OSErr
  59. NCRStartScript(
  60.         AddressSpaceID            addressSpaceID,
  61.         IOCommandID                ioCommandID,
  62.         ParmBlkPtr                pb,
  63.         ScriptSelector            scriptSelector
  64.     )
  65. {
  66.         OSErr                    status;
  67.         PerRequestDataPtr        perRequestDataPtr;
  68.  
  69.         Trace(NCRStartScript);
  70.         /*
  71.          * When we support concurrent transactions, we'll get the current
  72.          * PerRequestData record from a pool. If there aren't any available,
  73.          * we'll hide this parameter block on a look-aside queue and return
  74.          * "in progress".
  75.          */
  76.         perRequestDataPtr = GLOBAL.perRequestDataPtr;
  77.         /*
  78.          * Clear out the volatile parts of the table.
  79.          */
  80.         CLEAR(REQUEST.scriptData);                /* Shared with the NCR chip        */
  81.         CLEAR(REQUEST.shadow);                    /* Current interrupt state        */
  82.         REQUEST.timerID = kInvalidID;            /* No watchdog timer id yet        */
  83.         REQUEST.addressSpaceID = addressSpaceID;
  84.         REQUEST.pb = pb;
  85.         REQUEST.ioCommandID = ioCommandID;
  86.         if (scriptSelector == kSCSICommandScript
  87.          && SCSI.targetID == kNCRMemoryTestBusID) {
  88.             scriptSelector = (SCSI.memTestPhysAddress == NULL)
  89.                         ? kSCSITestISRScript : kSCSITestMemoryScript;
  90.         }
  91.         REQUEST.scriptSelector = scriptSelector;
  92.         switch (scriptSelector) {
  93.         case kBusResetScript:
  94.             REQUEST.scriptPtr =
  95.                         ((UInt32) GLOBAL.scriptIOTable.physicalMapping[0])
  96.                         + kBusResetScript;
  97.             REQUEST.scriptBaseAddress =
  98.                         (UInt32) GLOBAL.scriptIOTable.physicalMapping[0];
  99.             REQUEST.scriptDataPtr = kInvalidPageAddress;
  100.             REQUEST.watchdogTimeout = kNoSCSITimeout; /* We have a standard timeout    */
  101.             status = noErr;
  102.             break;
  103.         case kSCSICommandScript:
  104.             REQUEST.watchdogTimeout = SCSI.watchdogTimeout;
  105.             REQUEST.scriptPtr =
  106.                         ((UInt32) GLOBAL.scriptIOTable.physicalMapping[0])
  107.                         + kSCSICommandScript;
  108.             REQUEST.scriptDataPtr =
  109.                         ((UInt32) REQUEST.perRequestIOTable.physicalMapping[0])
  110.                         + offsetof(PerRequestData, scriptData);
  111.             status = NCRSetupDoSCSIScript(perRequestDataPtr);
  112.             REQUEST.scriptBaseAddress =
  113.                         (UInt32) GLOBAL.scriptIOTable.physicalMapping[0];
  114.             break;
  115.         case kSCSIRundownScript:
  116.             status = NCRBusBusyCheck();
  117.             if (status == noErr) {
  118.                 REQUEST.watchdogTimeout =
  119.                     ((NCRDriverRundownParamPtr) pb)->watchdogTimeout;
  120.                 REQUEST.scriptPtr =
  121.                         ((UInt32) GLOBAL.scriptIOTable.physicalMapping[0])
  122.                         + kSCSIRundownScript;
  123.                 REQUEST.scriptDataPtr =
  124.                             ((UInt32) REQUEST.perRequestIOTable.physicalMapping[0])
  125.                             + offsetof(PerRequestData, scriptData);
  126.                 REQUEST.scriptBaseAddress =
  127.                             (UInt32) GLOBAL.scriptIOTable.physicalMapping[0];
  128.                 SCRIPT.idMsgByte = 0x0;                /* Bogus id                    */
  129.                 SCRIPT.statusByte = 0xFF;            /* Initialize to garbage    */
  130.                 SCRIPT.commandCompleteByte = 0xFF;    /* Initialize to garbage    */
  131.                 /*
  132.                  * Setup the scatter-gather tables.
  133.                  */
  134.                 SetTable(deviceIDTable,        0,                                0);
  135.                 SetTable(idMsgTable,        AddressOf(idMsgByte),            1);
  136.                 SetTable(commandTable,        AddressOf(scsiCommand),            0);
  137.                 SetTable(statusTable,        AddressOf(statusByte),            1);
  138.                 SetTable(completeTable,        AddressOf(commandCompleteByte),    1);
  139.                 SetTable(bitBucketInTable,    AddressOf(bitBucketInByte),        1);
  140.                 SetTable(bitBucketOutTable,    AddressOf(bitBucketOutByte),    1);
  141.                 SetTable(ignoredMsgTable,    AddressOf(ignoredMsgInByte),    1);
  142.             }
  143.             break;
  144.         case kSCSITestISRScript:
  145.             REQUEST.watchdogTimeout = SCSI.watchdogTimeout;
  146.             REQUEST.scriptDataPtr = kInvalidPageAddress;
  147.             status = NCRSetupTestISR(perRequestDataPtr);
  148.             break;
  149.         case kSCSITestMemoryScript:
  150.             REQUEST.watchdogTimeout = SCSI.watchdogTimeout;
  151.             REQUEST.scriptDataPtr = kInvalidPageAddress;
  152.             status = NCRSetupTestMemory(perRequestDataPtr);
  153.             break;
  154.         default:
  155.             status = paramErr;                    /* Can't happen                    */
  156.             break;
  157.         }
  158.         if (status == noErr) {
  159.             /*
  160.              * We are ready to start an NCR I/O operation. The actual work is
  161.              * done by the Secondary Interrupt Handler.
  162.              */
  163.             status = NCRQueueSecondaryInterrupt(perRequestDataPtr, kIORequestStart);
  164.             if (status == noErr)                /* If successful, we return        */
  165.                 status = kIOBusyStatus;            /* busy to the driver mainline    */
  166.         }
  167.         if (status != kIOBusyStatus) {
  168.             /*
  169.              * We can't start I/O -- make sure the user's buffer is un-prepared.
  170.              */
  171.             CheckStatus(status, "\pNCRStartScript");
  172.             CheckpointIOTable(&REQUEST.scsiIOTable);
  173.         }
  174.         return (status);
  175. }
  176.  
  177. /*
  178.  * Setup a SCSI request. pb->ioMisc points to the NCRSCSIParam structure.
  179.  */
  180. OSErr
  181. NCRSetupDoSCSIScript(
  182.         PerRequestDataPtr        perRequestDataPtr
  183.     )
  184. {
  185.         OSErr                    status;
  186.         ByteCount                scsiCommandLength;
  187.         UInt32                    ncrDeviceID;
  188.  
  189.         Trace(NCRSetupDoSCSIScript);
  190.         /*
  191.          * Check for valid parameters. The caller has already verified that PBRead
  192.          * or PBWrite matches SCSI.driverAction.
  193.          */
  194.         status = noErr;
  195.         PB.ioActCount = 0;
  196. #if 0 && USE_LOG_LIBRARY
  197.         WriteLogEntry(GLOBAL.logRecordPtr, 'StSc',
  198.             LogFormat7(
  199.                 kLogFormatAddress,
  200.                 kLogFormatSigned, kLogFormatSigned, kLogFormatSigned,
  201.                 kLogFormatAddress, kLogFormatUnsigned,
  202.                 kLogFormatAddress
  203.             ),
  204.             &SCSI,
  205.             (signed long) SCSI.targetID,
  206.             (signed long) SCSI.logicalUnitNumber,
  207.             (signed long) SCSI.driverAction,
  208.             PB.ioBuffer, PB.ioReqCount,
  209.             &SCSI.logicalUnitNumber
  210.         );
  211. #endif
  212.         if (SCSI.logicalUnitNumber != 0)            /* This should be ok now    */
  213.             status = scsiLUNInvalid;                /* LUN's are unsupported    */
  214.         else if (SCSI.targetID == GLOBAL.initiatorID || SCSI.targetID >= 8)
  215.             status = scsiTIDInvalid;                /* Invalid target ID        */
  216.         else if ((PB.ioReqCount & 0xFF000000) != 0)    /* Long transfer length        */
  217.             status = scsiRequestInvalid;            /* Longer than SCSI Max        */
  218.         else if (PB.ioBuffer != NULL && PB.ioReqCount == 0) {
  219.             status = scsiRequestInvalid;
  220.         }
  221.         else if (SCSI.driverAction != kNCRDriverNoDataPhase && PB.ioBuffer == NULL) {
  222.             status = paramErr;
  223.         }
  224.         if (status == noErr) {
  225.             scsiCommandLength = SetupSCSICommand(perRequestDataPtr);
  226.             if (scsiCommandLength == 0)                /* Invalid SCSI command?    */
  227.                 status = paramErr;
  228.         }
  229.         /*
  230.          * Here is where we prepare the SCSI and per-request tables for the
  231.          * next request. PrepareNewDMATransfer understands "no data" transfers.
  232.          */
  233.         if (status == noErr)
  234.             status = PrepareNewDMATransfer(perRequestDataPtr);
  235.         if (status == noErr) {
  236.             /*
  237.              * We now have physical addresses for all data, Bind the data area
  238.              * to the script Table elements. All SCSI bus to/from memory
  239.              * accesses are done using the MoveFrom script operator and a
  240.              * static table. This is rather ugly, but the alternative would
  241.              * require patching physical addresses into the script. As done
  242.              * here, the entire script is read-only (after the first call that
  243.              * resolves our hack labels).
  244.              *
  245.              * Note that all addresses that will be read by the NCR chip must
  246.              * be byte-swapped. (Hmm, strictly speaking, this is not quite
  247.              * correct: we can jumper pin 142 (BIT_LIT/) to set the chip into
  248.              * big-endian mode. By running the chip in "PCI-native"
  249.              * little-endian mode, we illustrate the byte-swap requirements.
  250.              */
  251.             ncrDeviceID = (ByteCount)
  252.                 ( (0 << 24)                    /* bytelane 3 SCNTL3 config bits    */
  253.                 | (SCSI.targetID << 16)        /* bytelane 2 Target ID                */
  254.                 | (0 << 8)                    /* bytelane 1 SFXFER Offset/Period    */
  255.                 | 0                            /* bytelane 0 Must be zero            */
  256.                 );
  257.             SCRIPT.idMsgByte = 0x80                    /* Disable disconnect        */
  258.                 | (SCSI.logicalUnitNumber & 0x07);    /* Stuff in the LUN            */
  259.             SCSI.statusByte = SCRIPT.statusByte = 0xFF;
  260.             SCSI.messageByte = SCRIPT.commandCompleteByte = 0xFF;
  261.             SCRIPT.bitBucketOutByte = kScsiMsgAbort; /* For MSGO phase            */
  262.             /*
  263.              * Setup the scatter-gather tables.
  264.              */
  265.             SetTable(deviceIDTable,        0,                            ncrDeviceID);
  266.             SetTable(idMsgTable,        AddressOf(idMsgByte),        1);
  267.             SetTable(commandTable,        AddressOf(scsiCommand),        scsiCommandLength);
  268.             SetTable(statusTable,        AddressOf(statusByte),        1);
  269.             SetTable(completeTable,        AddressOf(commandCompleteByte),    1);
  270.             SetTable(bitBucketInTable,    AddressOf(bitBucketInByte),        1);
  271.             SetTable(bitBucketOutTable,    AddressOf(bitBucketOutByte),    1);
  272.             SetTable(ignoredMsgTable,    AddressOf(ignoredMsgInByte),    1);
  273.         }
  274.         CheckStatus(status, "\pNCRSetupDoSCSIScript failure");
  275.         return (status);
  276. }
  277.  
  278. /*
  279.  * SetupSCSIComand determines the command length and copies the command to the
  280.  * per-request record. It returns 0 if the command length is incorrect.
  281.  */
  282. ByteCount
  283. SetupSCSICommand(
  284.         PerRequestDataPtr        perRequestDataPtr
  285.     )
  286. {
  287.         ByteCount                scsiCommandLength;
  288.  
  289.         Trace(SetupSCSICommand);
  290.         /*
  291.          * We will copy data from the parameter block into the per-request
  292.          * data area. While this simplifies debugging, it has an additional
  293.          * advantage: it cuts down on the number of PrepareMemoryForIO
  294.          * requests to the minimum (script, per-request table, and user data).
  295.          * The only limitation is that it prevents our device from working
  296.          * with a non-standard SCSI device whose command block is longer than
  297.          * 12 bytes.
  298.          */
  299.         scsiCommandLength = SCSI.scsiCommandLength;
  300.         if (scsiCommandLength == 0)
  301.             scsiCommandLength = SCSIGetCommandLength(SCSI.scsiCommand);
  302.         if (scsiCommandLength == 0
  303.          || scsiCommandLength > sizeof SCRIPT.scsiCommand)
  304.             scsiCommandLength = 0;
  305.         if (scsiCommandLength > 0) {
  306.             BlockCopy(SCSI.scsiCommand, SCRIPT.scsiCommand, scsiCommandLength);
  307.             /*
  308.              * Store the LUN information in the command block - this is needed
  309.              * for old devices that only examine the command block for LUN values.
  310.              * (On SCSI-II, the the LUN is stored in the identify message).
  311.              */
  312.             SCRIPT.scsiCommand[1] &= ~0xE0;
  313.             SCRIPT.scsiCommand[1] |= (SCSI.logicalUnitNumber & 0x07) << 5;
  314. #if 1 && USE_LOG_LIBRARY
  315.             {
  316.                 Str255                work;
  317.                 int                    i;
  318.                 
  319.                 work[0] = 0;
  320.                 AppendSigned(work, SCSI.targetID);
  321.                 AppendChar(work, ' ');
  322.                 AppendUnsigned(work,    /* Try to capture the block number    */
  323.                     ( (SCRIPT.scsiCommand[2] << 16)
  324.                     + (SCRIPT.scsiCommand[3] <<  8)
  325.                     + (SCRIPT.scsiCommand[4] <<  0)
  326.                     )
  327.                 );
  328.                 AppendChar(work, ' ');
  329.                 AppendUnsigned(work, PB.ioReqCount);
  330.                 AppendChar(work, ' ');
  331.                 for (i = 0; i < scsiCommandLength; i++) {
  332.                     AppendHexLeadingZeros(work, SCRIPT.scsiCommand[i], 2);
  333.                     if (i < (scsiCommandLength - 1))
  334.                         AppendChar(work, '.');
  335.                 }
  336.                 WriteLogEntry(GLOBAL.logRecordPtr, 'SCSI',
  337.                         LogStringFormat, work);
  338.             }
  339. #endif
  340.         }
  341.         return (scsiCommandLength);
  342. }
  343.  
  344. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  345.  * SCSIGetCommandLength looks at the first command byte. If it's in the ANSI-standard
  346.  * range, return the command length.
  347.  */
  348. ByteCount
  349. SCSIGetCommandLength(
  350.         const unsigned char        *cmdBlock
  351.     )
  352. {
  353.         ByteCount                result;
  354.  
  355.         Trace(SCSIGetCommandLength);
  356.         /*
  357.          * Look at the "group code" in the command operation. Return a parameter
  358.          * error for the reserved (3, 4) and vendor-specific command (6, 7)
  359.          * command groups. Otherwise, set the command length from the group code
  360.          * value as specified in the SCSI-II spec. Then, copy the command block
  361.          * into the parameter block (this centralizes everything for debugging
  362.          * convenience).
  363.          */
  364.         switch (cmdBlock[0] & 0xE0) {
  365.         case (0 << 5):    result = 6;        break;
  366.         case (1 << 5):
  367.         case (2 << 5):    result = 10;    break;
  368.         case (5 << 5):    result = 12;    break;
  369.         default:        result = 0;        break;        /* This results in an error    */
  370.         }
  371.         return (result);
  372. }
  373.  
  374. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  375.  * Setup the ISR test: create a one-instruction script "INT noErr" and return success.
  376.  */
  377. OSErr
  378. NCRSetupTestISR(
  379.         PerRequestDataPtr        perRequestDataPtr
  380.     )
  381. {
  382.         Trace(NCRSetupTestISR);
  383.         PB.ioActCount = 0;
  384.         CLEAR(REQUEST.memoryMoveScript);
  385.         REQUEST.memoryMoveScript[0] = kIntOpcode;
  386.         REQUEST.memoryMoveScript[1] = noErr;
  387.         REQUEST.scriptPtr =
  388.                 ((UInt32) REQUEST.perRequestIOTable.physicalMapping[0])
  389.                 + offsetof(PerRequestData, memoryMoveScript);
  390.         REQUEST.scriptBaseAddress = REQUEST.scriptPtr;
  391.         return (noErr);
  392. }
  393.  
  394. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  395.  * Setup the memory test: Make sure we have the necessary buffers, setup the memory
  396.  * move script, then PrepareMemoryForIO.
  397.  */
  398. OSErr
  399. NCRSetupTestMemory(
  400.         PerRequestDataPtr        perRequestDataPtr
  401.     )
  402. {
  403.         OSErr                    status;
  404.         
  405.         Trace(NCRSetupTestMemory);
  406.         PB.ioActCount = 0;
  407.         status = noErr;
  408.         if (PB.ioBuffer == NULL || SCSI.memTestPhysAddress == NULL)
  409.             status = paramErr;
  410.         if (status == noErr)
  411.             status = PrepareNewDMATransfer(perRequestDataPtr);
  412.         if (status == noErr)
  413.             status = CheckForContiguousPhysicalMapping(&REQUEST.scsiIOTable);
  414.         if (status == noErr) {
  415.             REQUEST.scriptPtr =
  416.                     ((UInt32) REQUEST.perRequestIOTable.physicalMapping[0])
  417.                     + offsetof(PerRequestData, memoryMoveScript);
  418.             REQUEST.memoryMoveScript[3] = kIntOpcode;
  419.             REQUEST.memoryMoveScript[4] = noErr;        /* noErr                    */
  420.             REQUEST.scriptBaseAddress = REQUEST.scriptPtr;
  421.         }
  422.         CheckStatus(status, "\pNCRSetupTestMemory");
  423.         return (status);
  424. }
  425.  
  426.